// ****************************************************************************
// @File: SmartPtr.h
//
// PURPOSE: Class for user to use GC
// AUTHOR:  Zj Ai
//
// NOTES: 
//		The part of the SmartPtr is revised from the version of Daniel Liu (liulele@ee.buaa.edu.cn)'s GC  
//
// HISTORY:
//
//   ------ Zj Ai  05/06/2012 creating the file
//
// @EndHeader@
// ****************************************************************************
#pragma once

#include "stdafx.h"
#include "Lock.h"
#include "SingleObjWrapper.h"
#include "WrapperManager.h"
#include "SmartPtrManager.h"
#include "LinkNode.h"
#include "SmartPtrContext.h"
#include <assert.h>

template<typename T>
class CSmartPtr : public LinkNode
{
public:
	// eg. CSmartPtr<int> p = new int;
	// or CSmartPtr<int> p(new int);
	CSmartPtr(T* arg = NULL)
	{
		Lock l;

		// set this object's size in order to let the SmartPtrManager knows.
		SetSize(sizeof(T));

		bool bInsertSuccess = false;
		CSmartPtrManager::GetInstance()->AddToManager(this, bInsertSuccess);
		if (!bInsertSuccess)
		{
			// TODO: throw exception
			assert(bInsertSuccess == true);
		}

		CWrapperManager::GetInstance()->WrapObject<T>(arg);

		m_pRawPointer = arg;
	}

	// eg. CSmartPtr<int> p = new int;
	// CSmartPtr<int> p2 = p;
	CSmartPtr(const CSmartPtr& obj)
	{
		Lock l;

		bool bInsertSuccess = false;
		CSmartPtrManager::GetInstance()->AddToManager(this, bInsertSuccess);
		if (!bInsertSuccess)
		{
			// TODO: throw exception
			assert(bInsertSuccess == true);
		}

		SetSize(sizeof(T));

		// as it is converted from a CSmartPtr object, the WrapObject will only add the ref count
		CWrapperManager::GetInstance()->WrapObject<T>(obj.m_pRawPointer);

		m_pRawPointer = obj.m_pRawPointer;
	}
	
	// assignment operations		
	// eg CSmartPtr<int> p = new int;
	// CSmartPtr<int> p2 = new int;
	// p2 = p;
	CSmartPtr operator=(const CSmartPtr<T>& obj)
	{
		Lock l;

		// self assign check
		if(this == &obj)//assign to self
		{
			return *this;
		}

		SetSize(sizeof(T));

		SwapInnerObjects<T>(this->m_pRawPointer, obj.m_pRawPointer);

		// store the raw pointer
		m_pRawPointer = obj.m_pRawPointer;

		return *this;
	}

	// assignment operations		
	// eg CSmartPtr<int> p = new int;
	// CSmartPtr<int> p2 = new int;
	// p2 = new int;
	// int * p3 = new int;
	// p2 = p3;
	CSmartPtr operator=(T* pointer)
	{
		Lock l;

		SetSize(sizeof(T));

		SwapInnerObjects<T>(this->m_pRawPointer, pointer);

		// store the raw pointer
		m_pRawPointer = pointer;

		return *this;
	}

	// implicit transfer from CSmartPtr<T> to T*
	operator const T* () const
	{
		return m_pRawPointer;
	}

	operator T* () 
	{
		return m_pRawPointer;
	}

	// de-constructor
	~CSmartPtr(void)
	{
		Lock l;
		bool bRemoveSuccess = false;
		CSmartPtrManager::GetInstance()->RemveFromManager(this, bRemoveSuccess);
		//if (!bRemoveSuccess)
		//{
		//	// TODO: throw exception
		//	assert(bRemoveSuccess == true);
		//}

		if ( CSmartPtrContext::IsCollecting() )
		{
			return;
		}

		CWrapperManager::GetInstance()->Release(this->m_pRawPointer);
	}

	// for compare operations
	// it is not necessary to compare the SmartPtr<T>*, for there is an implicit transfer
	bool operator==(void* pointer)
	{
		Lock l;
		return pointer == static_cast<void*>(m_pRawPointer);
	}

	bool operator!=(void* pointer)
	{
		return !(pointer == static_cast<void*>(m_pRawPointer));
	}

	// in order to support the following statements
	// CSmartPtr<SomeClass> smartPtr = new SomeClass;
	// smartPtr->foo();
	// it can also support for polymorph
	T* operator->() {return m_pRawPointer;}
	const T* operator->() const {return m_pRawPointer;}

	// test only
	int GetRefCount()
	{
		return CWrapperManager::GetInstance()->GetRefCount(m_pRawPointer);
	}

private:
	template<typename T>
	void SwapInnerObjects(T* oldRawPointer, T* newRawPointer)
	{
		// release the old one, by decease the ref count
		CWrapperManager::GetInstance()->Release(oldRawPointer);

		// change the objects and add the ref count
		CWrapperManager::GetInstance()->WrapObject<T>(newRawPointer);
	}
	
private:
	T* m_pRawPointer;
};

template<typename DESTINATION, typename SOURCE>
CSmartPtr<DESTINATION> ptr_static_cast(CSmartPtr<SOURCE> ptr) 
{ 
	return static_cast<DESTINATION*>((SOURCE*)ptr); 
}

template<typename DESTINATION, typename SOURCE>
CSmartPtr<DESTINATION> ptr_dynamic_cast(CSmartPtr<SOURCE> ptr) 
{ 
	return dynamic_cast<DESTINATION*>((SOURCE*)ptr); 
}
